// -*- C++ -*------------------------------------------------------------------
//  $Id: Spacer.h,v 1.20 2008-08-03 21:26:37 biocomp Exp $
//
//  Class:              Spacer
//
//  Base Class(es):     Identity, Bond
//
//  Derived Class(es):  -
//
//  Containing:         Group, AminoAcid
//
//  Author:             Silvio Tosatto
//
//  Project Name:       Victor
//
//  Date:               08/99
//
//  Reviewed By:        <Name>
//
//  Description:
//    This class implements a "Spacer" or protein fragment. 
//  ***Attention***: The current implementation allows for "1 to 1" spacers,
//    ie. spacers composed of a single aminoacid chain. Both spacers composed 
//    of more than one aminoacid chain (eg. 2 beta sheet parts) and spacers
//    recursively composed of other spacers are *not* supported yet! 
//
// ---------------------------------------------------------------------------
 
#ifndef _SPACER_H_
#define _SPACER_H_


// Includes:
#include <Bond.h>
#include <Polymer.h>
#include <AminoAcid.h>
#include <Visitor.h>
#include <AminoAcidCode.h>
//#include <CaPositionMap.h>


// Global constants, typedefs, etc. (to avoid):

namespace Biopool {

//class CaPositionMap;

class Spacer : public Polymer
{
public: 
  //AK11
  friend class AtomEnergyCalculator;
  friend class SideChainPlacement;
  
  // CONSTRUCTORS/DESTRUCTOR:
  Spacer();
  Spacer(const Spacer& orig); 
  virtual ~Spacer(); 
  
  // PREDICATES:

  int getStartOffset() { return startOffset; }
  int getAtomStartOffset() { return startAtomOffset; }
  int getIndexFromPdbNumber(int index);  // in: pdb_num, out: array_num
  int getPdbNumberFromIndex(int index);  // in: array_num, out: pdb_num
  unsigned int maxPdbNumber() { 
    return startOffset + gaps.size() + sizeAmino(); }
  bool isGap(int index);
  void printGaps();
  unsigned int sizeGaps()  { return gaps.size(); }

  virtual string getClassName() const
    { return "Spacer"; }
  void save(Saver& s);   // data saver
  AminoAcid& getAmino(unsigned int n);
  const AminoAcid& getAmino(unsigned int n) const;
  const unsigned int  sizeAmino() const;
  
  Spacer& getSpacer(unsigned int n);  
  const Spacer& getSpacer(unsigned int n) const; 
  const unsigned int sizeSpacer() const;

  vector<pair<unsigned int, unsigned int> > getHelixData();
  vector<pair<unsigned int, unsigned int> > getStrandData();

  // MODIFIERS:
  void setStartOffset(int _offset);
  void setAtomStartOffset(int _offset) { startAtomOffset = _offset; }

  void addGap(int index);
  void removeGap(int index);
  void removeAllGaps();

  ///inserts a new aminoacid after given position setting given torsion angles and atomic distances
  void insertAminoAfter(string code, unsigned int n = 9999, double phi = -62, 
			double psi = -41, double omega = 180, 
			double NToCaLen = 1.458, double CaToCLen = 1.52, double CToOLen = 1.231, 
			double atCaToCAng = 111.6, double CaToOAng = 120.80,
			double CtoNLen = 1.33, double atCToNAng = 116.4, 
			double OToNAng = 123.2, double atNToCaAng = 121.9);
  void insertAminoAfterWithGaps(string code, unsigned int n = 9999, double phi = -62, 
			double psi = -41, double omega = 180,
			int beginHole=0, int endHole=0,
			vgVector3 <double> ca1=vgVector3<double>(), vgVector3 <double> ca2=vgVector3<double>(),
			string target="", Spacer* refSpacer=0, Spacer* pOriginalSpacer=0,
			double NToCaLen = 1.458, double CaToCLen = 1.52, double CToOLen = 1.231, 
			double atCaToCAng = 111.6, double CaToOAng = 120.80,
			double CtoNLen = 1.33, double atCToNAng = 116.4, 
			double OToNAng = 123.2, double atNToCaAng = 121.9 );
  ///inserts a new aminoacid before given position setting given torsion angles and atomic distances
  void insertAminoBefore(string code, unsigned int p = 0, double phi = -62, 
			 double psi = -41, double omega = 180,
			 double NToCaLen = 1.458, double CaToCLen = 1.52, double CToOLen = 1.231, 
			 double atCaToCAng = 111.6, double CaToOAng = 120.80,
			 double CtoNLen = 1.33, double atCToNAng = 116.4, 
			 double OToNAng = 123.2, double atNToCaAng = 121.9);
  ///inserts first aminoacid in an empty spacer copying it from another given aminoacid
  void insertFirstAmino(AminoAcid& amino, double phi = -62, double psi = -41, 
			double omega = 180);
  ///inserts first aminoacid in an empty spacer starting from code and interatomic distances
  void insertFirstAmino(string code, double phi = -62, double psi = -41, double omega = 180,
			double NToCaLen = 1.458, double CaToCLen = 1.52, double CToOLen = 1.231, 
			double atCaToCAng = 111.6, double CaToOAng = 120.80 );
  ///inserts first aminoacid in an empty spacer using position of Ca atoms
  void insertFirstAmino(vgVector3 <double> ca1, vgVector3 <double> ca2,
			string target, Spacer* refSpacer, double angleSeed, 
			double phi, double psi, double omega, vector< pair<int,int> > holes );
  ///insert a new sub spacer inside a spacer structure.
  ///ATTENTION: this method works only with superior spacers which are made of other subspacers 
  ///only (child components must be all spacers, the presence of single aminoacids is not considered)
  ///(This assumption is sufficient for the Thor module)
  void insertSubSpacerAfter(Spacer* sp, unsigned int pos);
  ///insert a new sub spacer inside an empty spacer structure
  ///ATTENTION: this method works only with superior spacers which are made of other subspacers 
  ///only (child components must be all spacers, the presence of aminoacids is not considered)
  ///(This assumption is sufficient for the Thor module)
  void insertFirstSpacer(Spacer* sp);
  //void refineStructure( Biopool::CaPositionMap* pm, double delta );

  void connectIn(Component* c);
  void connectOut(Component* c);
  
  virtual Component* unconnectIn();
  virtual Component* unconnectOut();

  void insertComponent(Component* c);
  void removeComponent(Component* c);
  //-------------
  void removeComponentFromIndex(unsigned int i);
  //--------------
  void deleteComponent(Component* c);

  void mergeSpacer( Spacer* s);
  Spacer* splitSpacer( unsigned int index, unsigned int count);

  void copy(const Spacer& orig);

  void load(Loader& l);  // data loader

  void setTrans(vgVector3<double> t);
  void addTrans(vgVector3<double> t);
  void setRot(vgMatrix3<double> r);
  void addRot(vgMatrix3<double> r);
  void sync(); // synchronize coords with structure

  void setStateFromSecondary(string sec);  // sets states from secondary string 
  void setStateFromTorsionAngles();  // sets states from its phi/psi angles

  const Spacer& getInBond(unsigned int n) const;
  Spacer& getInBond(unsigned int n);
  const Spacer& getOutBond(unsigned int n) const;
  Spacer& getOutBond(unsigned int n);

  void  makeFlat();
  void  groupLikeStateCode();

  virtual const Atom& getOpenInBondRef(unsigned int n = 0) const;
  virtual Atom& getOpenInBondRef(unsigned int n = 0);
  virtual const Atom& getOpenOutBondRef(unsigned int n = 0) const;
  virtual Atom& getOpenOutBondRef(unsigned int n = 0);

  virtual Component* clone();

  virtual void acceptCalculator(EnergyVisitor* v);
  virtual void acceptOptimizer(OptimizationVisitor* v);
  
  // OPERATORS:
  Spacer& operator=(const Spacer& orig);

  // TESTERS AND DEVELOPERS
  void printSubSpacerList();
  void printComponents();
  vector< pair<int,int> > getHoles();

  static double BOND_ANGLE_AT_CPRIME_TO_N;
  static double BOND_LENGTH_CPRIME_TO_N;
  static double BOND_ANGLE_CA_TO_O;
  static double BOND_LENGTH_C_TO_O;
  static double BOND_LENGTH_CALPHA_TO_CPRIME;
  static double BOND_LENGTH_N_TO_CALPHA;
  static double BOND_ANGLE_AT_CALPHA_TO_CPRIME;
  static double BOND_ANGLE_O_TO_N;
  static double BOND_ANGLE_AT_N_TO_CALPHA;
  
  // 
  static bool NMRGetMinimumCADistanceVector( const string &strFileName, vector<double> *pNMR );
    
protected:

  // HELPERS:
  void resetBoundaries();
  void pInsertComponentFront(Component* c);
  void modifySubSpacerList(Spacer* ,int);
  void alignSpacerFront(Spacer * s);
  void updateSubSpacerList();  

  // ATTRIBUTES

  int startOffset;
  int startAtomOffset;
  vector<int> gaps;

  pair<unsigned int, unsigned int> getSubSpacerListEntry(unsigned int);
  pair<unsigned int, unsigned int> getSubSpacerListEntry(unsigned int) const;

 
private:
  vector<pair<unsigned int, unsigned int> > subSpacerList;  
};

// ---------------------------------------------------------------------------
//                                    Spacer
// -----------------x-------------------x-------------------x-----------------

// PREDICATES:

inline void 
Spacer::save(Saver& s)
{
  s.saveSpacer(*this);
}

inline const unsigned int 
Spacer::sizeSpacer() const
{
  return subSpacerList.size();
}

// MODIFIERS:

inline void 
Spacer::removeComponent(Component* c)
{
  Polymer::removeComponent(c);
  setModified();
}
//------------------------------
inline void 
Spacer::removeComponentFromIndex(unsigned int i)
{
  Polymer::removeComponentFromIndex(i);
  setModified();
}
//------------------------------
inline void 
Spacer::deleteComponent(Component* c)
{
  Polymer::deleteComponent(c);
  setModified();
}


inline void 
Spacer::load(Loader& l)
{
  l.loadSpacer(*this);
  resetBoundaries();
}


inline void 
Spacer::setTrans(vgVector3<double> t)
{
  if (sizeAmino())
    getAmino(0).setTrans(t);
}

inline void 
Spacer::addTrans(vgVector3<double> t)
{
  if (sizeAmino())
    getAmino(0).addTrans(t);
}

inline void 
Spacer::setRot(vgMatrix3<double> r)
{
  if (sizeAmino())
    getAmino(0).setRot(r);
}

inline void 
Spacer::addRot(vgMatrix3<double> r)
{
  if (sizeAmino())
    getAmino(0).addRot(r);
}


inline const Spacer& 
Spacer::getInBond(unsigned int n) const
{
  return dynamic_cast<const Spacer&>(Bond::getInBond(n));
}

inline Spacer& 
Spacer::getInBond(unsigned int n)
{
  return dynamic_cast<Spacer&>(Bond::getInBond(n));
}

inline const Spacer& 
Spacer::getOutBond(unsigned int n) const
{
  return dynamic_cast<const Spacer&>(Bond::getOutBond(n));
}

inline Spacer& 
Spacer::getOutBond(unsigned int n)
{
  return dynamic_cast<Spacer&>(Bond::getOutBond(n));
}

inline void 
Spacer::acceptCalculator(EnergyVisitor* v)
{
  v->PrepareSpacer(*this);
}

inline void
Spacer::acceptOptimizer(OptimizationVisitor* v)
{
  v->PrepareSpacer(*this);
}


// OPERATORS:

// HELPERS:

} // namespace
#endif //_SPACER_H_
